package com.tianji.learning.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.enums.PointsRecordType;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static com.tianji.common.constants.MqConstants.Exchange.POINT_RECORD_EXCHANGE;

/**
 * @Author: 19311
 * @CreateTime: 2025-03-28
 * @Description: 签到记录服务实现类
 * @Version: 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {

    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;

    /**
     * 1、签到（Day7 - 课堂1）
     *
     * @return
     */
    @Override
    public SignResultVO sign() {
        SignResultVO signResultVO = new SignResultVO();
        // 1、签到
        // 1.1、获取当前用户专属的redis签到key
        String key = getSignKey();

        // 1.2、计算redis的offset值
        long offset = LocalDate.now().getDayOfMonth() - 1;

        // 1.3、直接签到，管它重不重复
        redisTemplate.opsForValue().setBit(key, offset, true);

        // 2、计算连续签到最大天数
        Integer maxDays = convertMaxDays(key, LocalDate.now().getDayOfMonth());
        signResultVO.setSignDays(maxDays);

        // 3、计算签到奖励积分
        if (maxDays >= 7 && maxDays < 14) {
            signResultVO.setRewardPoints(10);
        }

        if (maxDays >= 14 && maxDays < 28) {
            signResultVO.setRewardPoints(20);
        }

        if (maxDays >= 28) {
            signResultVO.setRewardPoints(40);
        }

        // 5、发送保存积分明细的消息（埋点）
        // 给积分变化队列监听器发送消息，记录到当前用户中，加分
        PointsRecord pointsRecord = new PointsRecord();
        pointsRecord.setPoints(signResultVO.getSignPoints() + signResultVO.getRewardPoints());
        pointsRecord.setType(PointsRecordType.SIGN);
        pointsRecord.setUserId(UserContext.getUser());
        mqHelper.sendAsyn(POINT_RECORD_EXCHANGE, "points.sign.change", pointsRecord);

        // 6、返回vo
        return signResultVO;

    }

    private String getSignKey() {
        // 键为月份加用户id
        //1、获取当前登录用户ID
        Long userId = UserContext.getUser();

        //2、获取当前日期
        String dateStr = LocalDate.now().format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);

        //3、拼接key并返回
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId + dateStr;
        return key;
    }

    /**
     * 根据bitmap的位图数据，转换成目前的最大签到天数
     */
    private Integer convertMaxDays(String key, int len) {
        List<Long> longs = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(
                        BitFieldSubCommands.BitFieldType.unsigned(len)).valueAt(0));
        if (ObjectUtils.isEmpty(longs) || longs.get(0) == null) {
            return 0;
        }
        long src = longs.get(0);
        Integer res = 0;
        while ((src & 1) != 0) {
            res++;
            src = src >> 1;
        }
        return res;
    }


    /**
     * 2、查询当月截止到当天的签到记录（Day7 - 课堂2）
     *
     * @return
     */
    @Override
    public Byte[] querySignRecords() {
        // 1、签到
        // 1.1、获取当前用户专属的redis签到key
        String key = getSignKey();

        // 1.2、计算redis的offset值
        long offset = LocalDate.now().getDayOfMonth() - 1;
        int len = (int) offset + 1;
        List<Long> longs = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(
                        BitFieldSubCommands.BitFieldType.unsigned(len)).valueAt(0));
        if (ObjectUtils.isEmpty(longs) || longs.get(0) == null) {
            return null;
        }
        Byte[] res = new Byte[len];
        long src = longs.get(0);
        while (len != 0) {
            res[len - 1] = (byte) (src & 1);
            src = src >> 1;
            len--;
        }
        return res;
    }

}
